{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Shadows with pythreejs\n",
    "\n",
    "This example is meant to demonstrate how to set up shadows with pythreejs. It is mainly based on the example code you can find in the three.js documentation, but is adapted to highlight some of the nuances of using it from pythreejs."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "First, we set up an example scene for exploring shadow behavior."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pythreejs import *\n",
    "import ipywidgets\n",
    "from IPython.display import display"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "view_width = 800\n",
    "view_height = 600"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create some example geometry in nice coder colors:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sphere = Mesh(\n",
    "    SphereBufferGeometry(1, 32, 16),\n",
    "    MeshStandardMaterial(color='red')\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cube = Mesh(\n",
    "    BoxBufferGeometry(1, 1, 1),\n",
    "    MeshPhysicalMaterial(color='green'),\n",
    "    position=[2, 0, 4]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plane = Mesh(\n",
    "    PlaneBufferGeometry(10, 10),\n",
    "    MeshPhysicalMaterial(color='gray'),\n",
    "    position=[0, -2, 0],)\n",
    "plane.rotation = (-3.14/2, 0, 0, 'XYZ')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create camera and lighting:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "camera = PerspectiveCamera( position=[10, 6, 10], aspect=view_width/view_height)\n",
    "key_light = SpotLight(position=[0, 10, 10], angle = 0.3, penumbra = 0.1)\n",
    "ambient_light = AmbientLight()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scene = Scene(children=[sphere, cube, plane, camera, key_light, ambient_light])\n",
    "controller = OrbitControls(controlling=camera)\n",
    "renderer = Renderer(camera=camera, scene=scene, controls=[controller],\n",
    "                    width=view_width, height=view_height, antialias=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "renderer"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Configuring shadows\n",
    "\n",
    "Now we can start playing around with the shadows in such a way that the results of the different options are immediately shown in the rendered scene."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First, set the spot light to track the cube position:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "key_light.target = cube"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Turn on shadows in the renderer:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "renderer.shadowMap.enabled = True\n",
    "renderer.shadowMap.type = 'PCFSoftShadowMap'  # default PCFShadowMap"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Even with shadow maps enabled, there are still no shadows. This is because three.js only includes those lights and objects that has been explicitly marked for shadows in its calculations. Let's turn on some of these:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Enable shadows for the light\n",
    "key_light.castShadow = True\n",
    "\n",
    "# Enable casting/receiving shadows for some objects:\n",
    "sphere.castShadow = True\n",
    "cube.castShadow = True\n",
    "plane.receiveShadow = True"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's move the cube closer to the sphere:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cube.position = (0, 1, 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the light changed to track the position of the cube. It is also clear that the shadow from the cube is not being taken into account on the sphere. As before, we can turn this on with `receiveShadow` on the sphere, but we also need to mark the sphere material for an update. This is needed for any shadow changes *after the first frame with shadows* is rendered."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Also enable sphere to receive shadow:\n",
    "sphere.receiveShadow = True\n",
    "sphere.material.needsUpdate = True"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, let's zoom in on the details of the shadows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "camera.position = [2.92, 1.75, 2.92]\n",
    "camera.quaternion = [-0.18, 0.38, 0.076, 0.90]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here, we can see that there is some pixelation of the shadows (although it is smoothed by using `PCFSoftShadowMap`). This can be fixed by increasing the resolution of the shadow map:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "key_light.shadow.mapSize = (2048, 2048)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
